﻿using Sasa.QualityTools.CodeAnalysis.Common;
using Sasa.QualityTools.CodeAnalysis.FxCopIntegrator.ViewModels.Events;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;

namespace Sasa.QualityTools.CodeAnalysis.FxCopIntegrator.ViewModels
{
    public class CodeAnalysisProgressViewModel : ViewModelBase
    {
        public event EventHandler CodeAnalysisCompleted;

        public event EventHandler CodeAnalysisCanceled;

        public event EventHandler CloseRequired;

        public event EventHandler<CodeAnalysisFailedEventArgs> CodeAnalysisFailed;

        private BackgroundWorker worker;

        private CodeAnalysisStatus status;

        private Version fxcopVerison;

        private Version metricsVersion;


        public ICollection<ICodeAnalyzerViewModel> CodeAnalyzers { get; private set; }

        public CodeAnalysisStatus Status
        {
            get
            {
                return status;
            }
            set
            {
                status = value;
                OnPropertyChanged("Status");
            }
        }

        public Version FxCopVersion
        {
            get
            {
                return fxcopVerison;
            }
            set
            {
                fxcopVerison = value;
                OnPropertyChanged("FxCopVersion");
            }
        }

        public Version MetricsVersion
        {
            get
            {
                return metricsVersion;
            }
            set
            {
                metricsVersion = value;
                OnPropertyChanged("MetricsVersion");
            }
        }

        public RelayCommand CancelCommand { get; private set; }

        public RelayCommand CloseCommand { get; private set; }


        public CodeAnalysisProgressViewModel()
        {
            CodeAnalyzers = new ObservableCollection<ICodeAnalyzerViewModel>();
            Status = CodeAnalysisStatus.Pending;

            CancelCommand = new RelayCommand(o =>
            {
                if (worker != null && worker.IsBusy)
                {
                    worker.CancelAsync();
                }
            });
            CloseCommand = new RelayCommand(o => OnCloseRequired(EventArgs.Empty));
        }


        public void PerformCodeAnalysis()
        {
            Logger.Info("Performing code analysis.");

            Status = CodeAnalysisStatus.Analyzing;

            worker = new BackgroundWorker();
            worker.WorkerSupportsCancellation = true;
            worker.DoWork += new DoWorkEventHandler(OnCodeAnalysisPerformed);
            worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(OnCodeAnalysisCompleted);
            worker.RunWorkerAsync();
        }

        private void OnCodeAnalysisPerformed(object sender, DoWorkEventArgs e)
        {
            foreach (var analyzer in CodeAnalyzers)
            {
                if (analyzer.Status == CodeAnalysisStatus.Skip)
                {
                    continue;
                }

                if (analyzer.Status == CodeAnalysisStatus.CompletedOnBuildTime)
                {
                    analyzer.LoadCodeAnalysisReport();
                }
                else
                {
                    analyzer.PerformCodeAnalysis();
                }
                if (worker.CancellationPending)
                {
                    e.Cancel = true;
                    break;
                }
            }
        }

        private void OnCodeAnalysisCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            if (e.Error != null)
            {
                Logger.Error(e.Error, "Failed to analyze because an exception was thrown unexpectedly.");
                Status = CodeAnalysisStatus.Failed;
                throw e.Error;
            }
            
            if (e.Cancelled)
            {
                Logger.Warn("Canceled code analysis.");
                Status = CodeAnalysisStatus.Canceled;
                OnCodeAnalysisCanceled(EventArgs.Empty);
                return;
            }

            var failedAnalyses = CodeAnalyzers.Where(o => o.Status == CodeAnalysisStatus.Failed);
            if (failedAnalyses.Count() > 0)
            {
                Logger.Warn("Occurred some errros when FxCop Integrator performed code analysis.");
                Status = CodeAnalysisStatus.Failed;
                OnCodeAnalysisFailed(new CodeAnalysisFailedEventArgs(failedAnalyses.Select(o => o.Target)));
                return;
            }

            if (CodeAnalyzers.Count(o => o.Status == CodeAnalysisStatus.Warning) > 0)
            {
                Logger.Warn("FxCopCmd returns at least one warning.");
                Status = CodeAnalysisStatus.Warning;
                return;
            }

            Logger.Info("Completed code analysis.");
            Status = CodeAnalysisStatus.Completed;

            OnCodeAnalysisCompleted(EventArgs.Empty);
        }

        protected virtual void OnCodeAnalysisCompleted(EventArgs e)
        {
            if (CodeAnalysisCompleted != null)
            {
                CodeAnalysisCompleted(this, e);
            }
        }

        protected virtual void OnCodeAnalysisFailed(CodeAnalysisFailedEventArgs e)
        {
            if (CodeAnalysisFailed != null)
            {
                CodeAnalysisFailed(this, e);
            }
        }

        protected virtual void OnCodeAnalysisCanceled(EventArgs e)
        {
            if (CodeAnalysisCanceled != null)
            {
                CodeAnalysisCanceled(this, e);
            }
        }

        protected virtual void OnCloseRequired(EventArgs e)
        {
            if (CloseRequired != null)
            {
                CloseRequired(this, e);
            }
        }
    }
}
